#include "SurfaceOpenGLES.h"

namespace YoghurtGum
{

	SurfaceOpenGLES::SurfaceOpenGLES(TextureOpenGLES* a_Tex, int a_Width, int a_Height) : Surface(a_Width, a_Height)
	{
		m_Texture = a_Tex;

		Init(a_Width, a_Height);

		Lock();
			Fill(Color(1.f, 0.f, 0.f));
		Unlock();
	}

	SurfaceOpenGLES::~SurfaceOpenGLES()
	{

	}

	void SurfaceOpenGLES::Init(int a_Width, int a_Height)
	{
		m_Width = a_Width;
		m_Height = a_Height;

		Log::GetSingleton().Print("Dimensions: (%i, %i)", m_Width, m_Height);

		GLfixed uvx, uvy;
		GLfixed vx, vy;

		if (!RenderMethodOpenGLES::Get()->NonPowerOfTwoTex())
		{
			GLsizei biggest = (a_Width > a_Height) ? a_Width : a_Height;

			GLsizei closest = 0;
			while (biggest >>= 1) { closest++; }
			closest = 1 << (closest + 1);

			m_FixedWidth = m_FixedHeight = GlIntToFixed(closest);

			m_TextureWidth = m_TextureHeight = closest;

			Log::GetSingleton().Print("Closest: (%i)", closest);

			uvx = GlFloatToFixed((float)(m_TextureWidth) / (float)closest);
			uvy = GlFloatToFixed((float)(m_TextureHeight) / (float)closest);

			//uvx = GlFloatToFixed((float)closest / (float)(m_TextureWidth));
			//uvy = GlFloatToFixed((float)closest / (float)(m_TextureHeight));

			//uvx = GlFloatToFixed(1.0);
			//uvy = GlFloatToFixed(1.0);

			//vx = GlIntToFixed(m_Width);
			//vy = GlIntToFixed(m_Height);
			vx = GlIntToFixed(m_TextureWidth);
			vy = GlIntToFixed(m_TextureHeight);

			Log::GetSingleton().Print("Surface dimensions: (%i, %i)", m_Width, m_Height);
			//Log::GetSingleton().Print("Surface UV: (%f, %f)", (float)(m_TextureWidth) / (float)closest, (float)(m_TextureHeight) / (float)closest);

			m_Pitch = closest;
		}
		else
		{
			m_FixedWidth = GlIntToFixed(m_Width);
			m_FixedHeight = GlIntToFixed(m_Height);

			m_TextureWidth = m_Width;
			m_TextureHeight = m_Height;

			uvx = GlIntToFixed(1);
			uvy = GlIntToFixed(1);

			vx = m_FixedWidth;
			vy = m_FixedHeight;

			m_Pitch = m_Width;
		}

		m_Vertices[ 0] = 0;   m_Vertices[ 1] = 0;
		m_Vertices[ 2] = vx;  m_Vertices[ 3] = 0;
		m_Vertices[ 4] = vx;  m_Vertices[ 5] = vy;

		m_Vertices[ 6] = 0;  m_Vertices[ 7] = 0;
		m_Vertices[ 8] = 0;  m_Vertices[ 9] = vy;
		m_Vertices[10] = vx; m_Vertices[11] = vy;

		m_UVs[ 0] = 0;    m_UVs[ 1] = 0;
		m_UVs[ 2] = uvx;  m_UVs[ 3] = 0;
		m_UVs[ 4] = uvx;  m_UVs[ 5] = uvy;

		m_UVs[ 6] = 0;    m_UVs[ 7] = 0;
		m_UVs[ 8] = 0;    m_UVs[ 9] = uvy;
		m_UVs[10] = uvx;  m_UVs[11] = uvy;

		glEnable(GL_TEXTURE_2D); CGLE();
		glBindTexture(GL_TEXTURE_2D, m_Texture->GetID()); CGLE();
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); CGLE();
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); CGLE();
			glTexImage2D(
				GL_TEXTURE_2D, 0, GL_RGBA, 
				m_TextureWidth, m_TextureHeight,
				0, 
				GL_RGBA, GL_UNSIGNED_BYTE, 0
			); CGLE();
		glDisable(GL_TEXTURE_2D); CGLE();

		Log::GetSingleton().Print("Surface texture: %i", m_Texture->GetID());

		m_Pixels = new YGC[m_Pitch * m_Height];
	}

	void SurfaceOpenGLES::Fill(Color a_Color)
	{
		if (!IsLocked()) { return; }

		//Log::GetSingleton().Print("Pixels %x", m_Pixels);

		YGC color = a_Color.GetColor();
		for (int y = 0; y < m_Height; y++) 
		{ 
			//Log::GetSingleton().Print("Row %i", y);
			for (int x = 0; x < m_Width; x++) 
			{
				//Log::GetSingleton().Print("Column %i", x);
				m_Pixels[y * m_Pitch + x] = color; 
			}
		}
	}

	void SurfaceOpenGLES::Resize(int a_Width, int a_Height)
	{
		Lock();

		delete m_Pixels;
		//if (glIsTexture) { glDeleteTextures(1, &m_Texture); }
		m_Texture->Reload();
		Init(a_Width, a_Height);

		Unlock();
	}

	bool SurfaceOpenGLES::DumpTo(Surface* a_Target)
	{
		YG_STUB;
		return false;
	}

	bool SurfaceOpenGLES::CopyTo(Surface* a_Target, int a_X, int a_Y)
	{
		YG_STUB;
		return false;
	}

	bool SurfaceOpenGLES::Draw(int a_X, int a_Y)
	{
		glShadeModel(GL_SMOOTH);
		glDisable(GL_DEPTH_TEST);
		glDisable(GL_LIGHTING);
		glDisable(GL_CULL_FACE);

		glEnable(GL_TEXTURE_2D); CGLE();
		glBindTexture(GL_TEXTURE_2D, m_Texture->GetID()); CGLE();

		glPushMatrix(); CGLE();
		glTranslatex(GlIntToFixed(a_X), GlIntToFixed(a_Y), 0); CGLE();

			glEnableClientState(GL_TEXTURE_COORD_ARRAY); CGLE();
			glEnableClientState(GL_VERTEX_ARRAY); CGLE();
				glVertexPointer(2, GL_FIXED, 0, m_Vertices); CGLE();
				glTexCoordPointer(2, GL_FIXED, 0, m_UVs); CGLE();
				glDrawArrays(GL_TRIANGLES, 0, 6); CGLE();
			glDisableClientState(GL_VERTEX_ARRAY); CGLE();
			glDisableClientState(GL_TEXTURE_COORD_ARRAY); CGLE();

		glPopMatrix(); CGLE();

		return true;
	}

	void SurfaceOpenGLES::Plot(int a_PosX, int a_PosY, Color a_Color)
	{
		if (!IsLocked()) { return; }

		if (
			a_PosX < 0 || a_PosX > m_Width - 1 ||
			a_PosY < 0 || a_PosY > m_Height - 1
		) 
		{ 
			return; 
		}

		m_Pixels[a_PosY * m_Width + a_PosX] = a_Color.GetColor();
	}

	void SurfaceOpenGLES::Line(float a_FromX, float a_FromY, float a_ToX, float a_ToY, Color a_Color)
	{
		if (!IsLocked()) { return; }
		
		//based on Jacco Bikker's implementation of the line drawing algorithm Dr. Bresenham developed

		if ((int)(a_FromX - a_ToX) != 0 || (int)(a_FromY - a_ToY) != 0)
		{ 
			YGC color = a_Color.GetColor();

			// are the steps determined by the x or the y axis?
			float xlength = Math::Abs(a_ToX - a_FromX);
			float ylength = Math::Abs(a_ToY - a_FromY);
			float length = (xlength > ylength) ? xlength : ylength;
			int steps = Math::Round(length);

			float stepX = 0;
			float stepY = 0;
			if (length > 0)
			{
				stepX = ((a_ToX - a_FromX) / steps);
				stepY = ((a_ToY - a_FromY) / steps);
			}

			// start at (x1, y1) go to (x2, y2)
			float drawX = a_FromX;
			float drawY = a_FromY;

			// does the line fall within the surface boundaries?
			if (
				(a_FromX >= 0 && a_FromX < m_Width  && a_ToX >= 0 && a_ToX < m_Width ) &&
				(a_FromY >= 0 && a_FromY < m_Height && a_ToY >= 0 && a_ToY < m_Height)
			)
			{
				// no need for per pixel checking!
				for (int i = 0; i <= steps; i++)
				{
					int plot = (Math::Round(drawY) * m_Pitch) + (int)(Math::Round(drawX));
					m_Pixels[plot] = color;

					//Plot(Math::Round(drawX), Math::Round(drawY), a_Color);

					drawX += stepX;
					drawY += stepY;
				}
			}
			else
			{
				for (int i = 0; i <= steps; i++)
				{
					// check if a pixel falls within our view
					// slower, but better line drawing around the edges
					if (drawX > 0 && drawX < m_Width &&
						drawY > 0 && drawY < m_Height)
					{
						int plot = (Math::Round(drawY) * m_Pitch) + (int)(Math::Round(drawX));
						m_Pixels[plot] = color;

						//Plot(Math::Round(drawX), Math::Round(drawY), a_Color);
					}

					drawX += stepX;
					drawY += stepY;
				}
			}
		}
	}

	void SurfaceOpenGLES::Square(int a_StartX, int a_StartY, int a_EndX, int a_EndY, Color a_Color)
	{
		if (!IsLocked()) { return; }

		int x1 = (a_StartX < 0) ? 0 : a_StartX;
		int y1 = (a_StartY < 0) ? 0 : a_StartY;
		int x2 = (a_EndX > m_Width - 1) ? m_Width - 1 : a_EndX;
		int y2 = (a_EndY > m_Height - 1) ? m_Height - 1 : a_EndY;

		YGC c = a_Color.GetColor();

		for (int y = y1; y < y2; y++)
		{
			for (int x = x1; x < x2; x++)
			{
				m_Pixels[y * m_Pitch + x] = c;
			}
		}
	}


	YGC* SurfaceOpenGLES::GetBuffer()
	{
		YG_STUB;
		return NULL;
	}

	void SurfaceOpenGLES::SetBuffer(YGC* a_Buffer)
	{
		YG_STUB;
	}

	void SurfaceOpenGLES::Lock()
	{
		m_Locked = true;
	}

	void SurfaceOpenGLES::Unlock()
	{
		m_Locked = false;

		glEnable(GL_TEXTURE_2D); CGLE();
			glBindTexture(GL_TEXTURE_2D, m_Texture->GetID()); CGLE();
			glTexSubImage2D(
				GL_TEXTURE_2D, 0,
				0, 0, 
				m_Pitch, m_Height,
				GL_RGBA, GL_UNSIGNED_BYTE, m_Pixels
			); CGLE();
		glDisable(GL_TEXTURE_2D); CGLE();
	}

}